package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 三种商品（VIP会员、专辑、声音）订单结算,渲染订单结算页面
     *
     * @param tradeVo (购买项目类型、购买项目ID、声音数量)
     * @return 订单VO信息
     */
    @Override
    public OrderInfoVo trade(TradeVo tradeVo) {
        //0.创建订单VO对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        Long userId = AuthContextHolder.getUserId();




        //1.初始化订单VO中价格、订单明细列表、订单减免明细列表
        //1.1 声明三个初始价格：原价、减免金额、订单金额
        BigDecimal originalAmount = new BigDecimal("0.00");
        BigDecimal orderAmount = new BigDecimal("0.00");
        BigDecimal derateAmount = new BigDecimal("0.00");
        //1.2 声明封装订单明细集合
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //1.3 声明封装订单减免明细集合
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        //获取购买项目类型 1001-专辑 1002-声音 1003-vip会员
        String itemType = tradeVo.getItemType();
        //2.处理购买项目类型：VIP套餐
        if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
            //2.1 远程调用用户服务，得到购买VIP会员套餐信息
            VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
            Assert.notNull(vipServiceConfig, "会员套餐{}不存在", tradeVo.getItemId());
            //2.2 计算商品价格：订单价=原价-减免金额
            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            if (originalAmount.compareTo(orderAmount) == 1) {
                derateAmount = originalAmount.subtract(orderAmount);
            }
            //2.3 将得到VIP套餐作为订单明细，封装成订单明细VO对象，加入到订单明细列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(vipServiceConfig.getId());
            orderDetailVo.setItemName("会员套餐：" + vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //2.4 如有优惠金额，封装订单减免vo对象，加入到订单减免列表
            if (originalAmount.compareTo(orderAmount) == 1) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("套餐限时减免：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
            //3.TODO 处理购买商品类型为：专辑
            //3. 处理购买项目类型：专辑
            //3.1 远程调用用户服务，判断用户是否已购买过专辑，如果已买则响应已购买
            Long albumId = tradeVo.getItemId();
            Boolean flag = userFeignClient.isPaidAlbum(albumId).getData();
            if (flag) {
                throw new GuiguException(500, "用户已购买专辑");
            }

            //3.2 远程调用专辑服务，获取专辑价格以及相关折扣(普通、VIP折扣)
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "专辑{}不存在", albumId);
            BigDecimal price = albumInfo.getPrice();
            BigDecimal discount = albumInfo.getDiscount();
            BigDecimal vipDiscount = albumInfo.getVipDiscount();

            //3.3 远程调用用户服务，判断当前用户是否是会员
            Boolean isVIP = false;

            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "用户{}不存在", userId);
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }

            //3.4 基于用户角色专辑折扣计算专辑价格(原价、订单价、减免价格)
            originalAmount = price;
            orderAmount = originalAmount;
            if (!isVIP && discount.doubleValue() != -1) {
                //专辑价格 普通用户折扣  订单价格=原价*折扣
                orderAmount =
                        originalAmount.multiply(discount).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }
            if (isVIP && vipDiscount.doubleValue() != -1) {
                //专辑价格 VIP会员折扣  订单价格=原价*折扣
                orderAmount =
                        originalAmount.multiply(vipDiscount).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }
            //计算专辑价格减免金额 = 原价-订单价格
            derateAmount = originalAmount.subtract(orderAmount);
            //3.5 将专辑信息作为订单明细，封装成订单明细VO对象，加入到订单明细列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(albumId);
            orderDetailVo.setItemName("专辑：" + albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //3.6 如有优惠金额，封装订单减免vo对象，加入到订单减免列表
            if (originalAmount.compareTo(orderAmount) == 1) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("专辑折扣减免：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }

        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
            //4.TODO 处理购买商品类型为：声音
            //4.处理购买项目类型：声音

            //4.1 远程调用专辑服务获取待购买声音列表
            Long trackId = tradeVo.getItemId();
            List<TrackInfo> trackInfoList = albumFeignClient.findWaitBuyTrackInfoList(trackId, tradeVo.getTrackCount()).getData();
            Assert.notNull(trackInfoList, "没有需要结算声音");

            //4.2 远程调用专辑服务获取声音单价-声音不支持折扣
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData();
            Assert.notNull(albumInfo, "专辑{}不存在", trackInfoList.get(0).getAlbumId());
            BigDecimal price = albumInfo.getPrice();

            //4.3 封装相关价格
            originalAmount = price.multiply(new BigDecimal(trackInfoList.size()));
            orderAmount = originalAmount;

            //4.4 将待购买声音作为订单明细，封装成订单明细VO对象，加入到订单明细列表
            orderDetailVoList = trackInfoList
                    .stream()
                    .map(trackInfo -> {
                        OrderDetailVo orderDetailVo = new OrderDetailVo();
                        orderDetailVo.setItemId(trackInfo.getId());
                        orderDetailVo.setItemName("声音：" + trackInfo.getTrackTitle());
                        orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                        orderDetailVo.setItemPrice(price);
                        return orderDetailVo;
                    }).collect(Collectors.toList());

        }
        //5.封装订单VO信息共有的信息，价格赋值、商品明细列表赋值、订单减免明细列表赋值、流水号机制、签名机制
        //5.1 封装第一类信息：三个价格（原价、订单价、减免价）
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setDerateAmount(derateAmount);

        //5.2 封装第二类信息：商品明细列表、优惠列表
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);

        //5.3 封装第三类信息：购买项目类型、流水号机制（本次订单流水号）、签名机制（时间戳+签名值）
        //5.3.1 购买项目类型
        orderInfoVo.setItemType(itemType);
        //5.3.2 流水号机制第一步：生成本次订单流水号，解决回退、重复点击提交造成订单重复提交问题
        //5.3.2.1. 构建当前用户本次订单流水号Key
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //5.3.2.2. 构建当前用户本次订单流水号UUID值
        String tradeNo = IdUtil.randomUUID();
        //5.3.2.3. 写入Redis有效时间5分钟
        redisTemplate.opsForValue().set(tradeKey, tradeNo, 5, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);

        //5.3.3 签名机制
        //5.3.3.1. 生成本次订单时间戳
        long timeMillis = System.currentTimeMillis();
        orderInfoVo.setTimestamp(timeMillis);
        //5.3.3.2 将订单VO转为Map，生成本次订单签名值 TODO 由于付款方式属性payWay为null不需要参与签名
        Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo, false, true);
        String sign = SignHelper.getSign(orderInfoMap);
        //5.3.3.3. 封装Vo中签名值
        orderInfoVo.setSign(sign);
        //6.响应订单vo，客户端完成订单页面渲染
        return orderInfoVo;
    }


    @Autowired
    private AccountFeignClient accountFeignClient;


    @Value("${order.cancel}")
    private int ttl;

    @Autowired
    private RabbitService rabbitService;


    /**
     * 提交订单
     *
     * @param userId      用户ID
     * @param orderInfoVo 订单vo信息
     * @return {orderNo:"订单编号"}
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> submitOrder(Long userId, OrderInfoVo orderInfoVo) {
        // 第一步：检查用户是否重复提交订单
        // 比如用户点击提交按钮后又按了浏览器的后退键，然后再次点击提交，这样就会重复下单
        // 我们用一个唯一的流水号来防止这种情况

        // 在Redis中存储的流水号的key
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;

        // 创建一个脚本，用来检查流水号是否正确，如果正确就删除它
        // 这样可以确保一个流水号只能用一次
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(script, Boolean.class);
        Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());

        // 如果流水号验证失败，说明可能是重复提交或者流水号被篡改
        if (!flag) {
            throw new GuiguException(500, "流水号校验失败请重试");
        }

        // 第二步：检查订单信息是否被恶意修改
        // 防止用户在提交订单时修改价格等重要信息来占便宜

        // 把订单信息转换成Map格式，方便进行签名验证
        Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo);
        // 移除支付方式字段，因为在生成签名时没有包含这个字段
        orderInfoMap.remove("payWay");
        // 验证签名是否正确，如果被篡改签名就会不匹配
        SignHelper.checkSign(orderInfoMap);

        // 第三步：把订单信息保存到数据库
        // 包括主订单表、订单详情表等，此时订单状态是"未支付"
        OrderInfo orderInfo = this.saveOrderInfo(userId, orderInfoVo);

        // 第四步：处理不同的支付方式
        // 如果用户选择用账户余额支付
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())) {

            // 4.1 从用户账户扣钱
            // 准备扣款信息
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderInfo.getOrderNo());      // 订单号
            accountDeductVo.setUserId(orderInfo.getUserId());        // 用户ID
            accountDeductVo.setAmount(orderInfo.getOrderAmount());   // 扣款金额
            accountDeductVo.setContent(orderInfo.getOrderTitle());   // 扣款说明

            // 调用账户服务来扣钱，需要检查是否扣款成功
            Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
            if (!result.getCode().equals(200)) {
                throw new GuiguException(500, "账户余额不足");
            }

            // 扣钱成功后，更新订单状态为"已支付"
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);

            // 4.2 发放虚拟商品给用户
            // 比如用户买的是VIP会员、积分、虚拟道具等，需要立即发放给用户

            // 准备发货信息
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());     // 订单号
            userPaidRecordVo.setUserId(orderInfo.getUserId());       // 用户ID
            userPaidRecordVo.setItemType(orderInfo.getItemType());   // 商品类型
            // 获取用户购买的所有商品ID列表
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream()
                    .map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);

            // 调用用户服务来发放虚拟商品
            result = userFeignClient.savePaidRecord(userPaidRecordVo);
            if (!result.getCode().equals(200)) {
                throw new GuiguException(500, "虚拟物品发货异常");
            }
        }

        // 第五步：如果用户选择微信支付
        // TODO: 这部分还没实现，需要处理微信支付和订单超时自动关闭
        if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(orderInfoVo.getPayWay())) {
            rabbitService.sendDealyMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderInfo.getId(), ttl);
        }

        // 最后返回订单号给前端，让用户知道订单创建成功
        Map<String, String> map = new HashMap<>();
        map.put("orderNo", orderInfo.getOrderNo());
        return map;
    }


    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    /**
     * 保存订单相关信息（订单、订单明细、订单减免）
     *
     * @param userId
     * @param orderInfoVo
     * @return
     */
    @Override
    public OrderInfo saveOrderInfo(Long userId, OrderInfoVo orderInfoVo) {

        // ============== 第一步：保存主订单信息 ==============

        // 1. 将前端传来的订单数据(OrderInfoVo)转换成数据库实体类(OrderInfo)
        // BeanUtil.copyProperties是一个工具方法，可以把一个对象的属性值复制到另一个对象
        // 就像把A盒子里的东西搬到B盒子里
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);

        // 1.1 设置订单状态为"未支付"
        // 新创建的订单默认都是未支付状态，用户还没有付钱
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);

        // 1.2 设置这个订单属于哪个用户
        // userId是从参数传进来的，表示是哪个用户下的订单
        orderInfo.setUserId(userId);

        // 1.3 设置订单名称（用第一个商品的名字作为订单标题）
        // 获取订单中包含的商品明细列表
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        // 检查商品列表是否不为空（CollectionUtil.isNotEmpty检查集合是否有内容）
        if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
            // 取第一个商品的名称作为整个订单的标题
            // 比如用户买了3本听书，就用第一本书的名字作为订单名称
            orderInfo.setOrderTitle(orderDetailVoList.get(0).getItemName());
        }

        // 1.4 生成全球唯一的订单编号（用于对接支付平台）
        // 订单编号格式：年月日 + 雪花算法生成的唯一ID
        // 例如：20240615 + 1234567890123456789
        // DateUtil.today()获取今天日期如"2024-06-15"，replaceAll("-", "")去掉横杠变成"20240615"
        // IdUtil.getSnowflake(1, 1).nextIdStr()用雪花算法生成唯一的长数字字符串
        String oderNo = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflake(1, 1).nextIdStr();
        orderInfo.setOrderNo(oderNo);

        // 将订单信息保存到数据库的订单表中
        orderInfoMapper.insert(orderInfo);

        // 获取刚刚保存的订单的ID（数据库自动生成的主键）
        // 这个ID后面要用来关联订单明细和减免信息
        Long orderId = orderInfo.getId();

        // ============== 第二步：保存订单明细列表（用户买了哪些具体商品）==============

        // 2.1 检查订单明细列表是否有内容
        if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
            // 遍历每一个商品明细，逐个保存到数据库
            // 就像用户买了3本听书，这里会循环3次，每次处理一本书的信息
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                // 将商品明细的VO对象转换成数据库实体对象
                OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);

                // 2.1 设置这个商品明细属于哪个订单
                // 通过orderId把商品和订单关联起来
                orderDetail.setOrderId(orderId);

                // 2.2 将商品明细保存到数据库的订单明细表中
                orderDetailMapper.insert(orderDetail);
            }
        }

        // ============== 第三步：保存订单减免列表（优惠券、折扣等）==============

        // 3. 获取订单的减免信息列表（比如用了优惠券、会员折扣等）
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();

        // 检查减免信息列表是否有内容
        if (CollectionUtil.isNotEmpty(orderDerateVoList)) {
            // 遍历每一个减免项目，逐个保存到数据库
            // 比如用户用了2张优惠券，这里会循环2次
            for (OrderDerateVo orderDerateVo : orderDerateVoList) {
                // 将减免信息的VO对象转换成数据库实体对象
                OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);

                // 3.1 设置这个减免信息属于哪个订单
                // 通过orderId把减免信息和订单关联起来
                orderDerate.setOrderId(orderId);

                // 3.2 将减免信息保存到数据库的订单减免表中
                orderDerateMapper.insert(orderDerate);
            }
        }

        // ============== 最后：返回保存完成的订单信息 ==============

        // 返回已经保存到数据库的完整订单信息
        // 这个orderInfo现在包含了数据库自动生成的ID等信息
        return orderInfo;
    }

    /**
     * 根据订单编号查询订单信息
     *
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        //1.根据订单编号查询订单信息
        LambdaUpdateWrapper<OrderInfo> orderInfoLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        orderInfoLambdaUpdateWrapper.eq(OrderInfo::getOrderNo, orderNo);
        OrderInfo orderInfo = orderInfoMapper.selectOne(orderInfoLambdaUpdateWrapper);
        if (orderInfo != null) {

            //2.根据订单ID查询订单明细列表
            LambdaUpdateWrapper<OrderDetail> orderDetailLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            orderDetailLambdaUpdateWrapper.eq(OrderDetail::getOrderId, orderInfo.getId());
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(orderDetailLambdaUpdateWrapper);
            if (CollectionUtil.isNotEmpty(orderDetailList)) {
                orderInfo.setOrderDetailList(orderDetailList);
            }
            //3.根据订单ID查询订单优惠列表
            LambdaUpdateWrapper<OrderDerate> orderDerateLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            orderDerateLambdaUpdateWrapper.eq(OrderDerate::getOrderId, orderInfo.getId());
            List<OrderDerate> orderDerateList = orderDerateMapper.selectList(orderDerateLambdaUpdateWrapper);
            if (CollectionUtil.isNotEmpty(orderDerateList)) {
                orderInfo.setOrderDerateList(orderDerateList);
            }
        }
        return orderInfo;
    }

    /**
     * 查询并且根据状态取消订单
     *
     * @param orderId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(Long orderId) {
        //1.根据订单ID查询订单信息
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        //2.判读支付状态 ：如果是未支付，则取消订单
        if (SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())) {
            //2.1 设置订单状态为已取消
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            //2.2 更新订单信息
            orderInfoMapper.updateById(orderInfo);
        }
    }


    /**
     * 查询当前用户订单列表
     *
     * @param userId
     * @param pageInfo
     * @return
     */
    @Override
    public Page<OrderInfo> getOrderByPage(Long userId, Page<OrderInfo> pageInfo) {
        //1.调用持久层动态SQL查询订单及订单明细
        pageInfo = orderInfoMapper.getOrderByPage(pageInfo, userId);
        return pageInfo;
    }


    /**
     * 用户支付成功后，修改订单状态：已支付
     *
     * @param orderNo
     */
    @Override
    public void orderPaySuccess(String orderNo) {
        //1.根据订单编号查询订单状态
        OrderInfo orderInfo = orderInfoMapper.selectOne(
                new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getOrderNo, orderNo)
        );
        //订单状态：0901-未支付 0902-已支付 0903-已取消
        String orderStatus = orderInfo.getOrderStatus();
        if (SystemConstant.ORDER_STATUS_UNPAID.equals(orderStatus)) {
            //2.如果订单状态是未支付：修改订单状态为已支付
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);

            //3.远程调用"用户服务"虚拟物品发货
            //3.1 构建虚拟物品发货vo对象
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderNo);
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            //查询订单明细得的购买项目ID列表
            List<OrderDetail> orderDetailList = orderDetailService.list(
                    new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderInfo.getId())
                            .select(OrderDetail::getItemId)
            );
            List<Long> itemIdList = orderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            //3.2 执行远程调用，一定要判断调用业务状态码
            Result result = userFeignClient.savePaidRecord(userPaidRecordVo);
            if (result.getCode().intValue() != 200) {
                throw new GuiguException(result.getCode(), result.getMessage());
            }
        }

    }
}
